package com.forest.datachangetracker.aspect;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.forest.datachangetracker.annotation.DataAudit;
import com.forest.datachangetracker.annotation.MultiDataAudit;
import com.forest.datachangetracker.context.AuditContext;
import com.forest.datachangetracker.context.AuditContextHolder;
import com.forest.datachangetracker.enums.OperationType;
import com.forest.datachangetracker.service.AuditLogService;
import com.forest.datachangetracker.service.IOperator;
import com.forest.datachangetracker.service.audit.AuditStrategy;
import com.forest.datachangetracker.service.audit.impl.DefaultAuditStrategy;
import com.forest.datachangetracker.utils.MapperCacheManager;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.javers.core.diff.Diff;
import org.javers.core.diff.changetype.ValueChange;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Aspect
@Component
public class DataAuditAspect {

    @Resource
    private AuditLogService auditLogService;

    @Resource(name = "dataChangeTrackerObjectMapper")
    private ObjectMapper objectMapper;

    @Resource
    private List<AuditStrategy> auditStrategies;

    @Resource
    private IOperator operator;

    private SpelExpressionParser spelParser = new SpelExpressionParser();
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    private final Map<String, Object> entityCache = new ConcurrentHashMap<>();

    @Around("@annotation(dataAudit)")
    public Object aroundSingleAuditMethod(ProceedingJoinPoint joinPoint, DataAudit dataAudit) throws Throwable {
        return processAudit(joinPoint, new DataAudit[]{dataAudit});
    }

    @Around("@annotation(multiDataAudit)")
    public Object aroundMultiAuditMethod(ProceedingJoinPoint joinPoint, MultiDataAudit multiDataAudit) throws Throwable {
        return processAudit(joinPoint, multiDataAudit.value());
    }

    /**
     * 处理审计逻辑
     *
     * @param joinPoint
     * @param dataAudits
     * @return
     * @throws Throwable
     */
    public Object processAudit(ProceedingJoinPoint joinPoint, DataAudit[] dataAudits) throws Throwable {
        // 准备审计上下文
        EvaluationContext evaluationContext = createEvaluationContext(joinPoint);
        AuditContext context = prepareMainContext(joinPoint, evaluationContext);
        Map<DataAudit, Object> oldEntities = preProcessOldEntities(dataAudits, context, evaluationContext);

        Object result = null;
        boolean success = true;
        String errorMessage = null;

        try {
            // 执行目标方法
            result = joinPoint.proceed();
            // 方法执行成功后处理审计逻辑
            // 更新评估上下文（包含返回值）
            evaluationContext = createEvaluationContext(joinPoint, result);
            context.setEvaluationContext(evaluationContext);
        } catch (Exception e) {
            success = false;
            errorMessage = e.getMessage();
            // 记录失败的审计日志
            for (DataAudit dataAudit : dataAudits) {
                if(dataAudit.async()) {
                    EvaluationContext evaluationContextRun = evaluationContext;
                    boolean successRun = success;
                    String message = errorMessage;
                    CompletableFuture.runAsync(() -> {
                        auditLogService.recordAuditLog(
                                createAuditContext(dataAudit, context, evaluationContextRun),
                                null, null, null, successRun, message
                        );
                    });
                } else {
                    auditLogService.recordAuditLog(
                            createAuditContext(dataAudit, context, evaluationContext),
                            null, null, null, success, errorMessage
                    );
                }
            }
            throw e;
        } finally {
            //清空线程变量
            AuditContextHolder.clear();
            //处理审计逻辑
            if(success) {
                // 处理审计逻辑
                for (DataAudit dataAudit : dataAudits) {
                    processSingleAudit(dataAudit, context, result, oldEntities.get(dataAudit));
                }
            }
        }
        return result;
    }

    private void processSingleAudit(DataAudit dataAudit, AuditContext mainContext, Object result, Object oldEntity) {
        try {
            AuditContext context = createAuditContext(dataAudit, mainContext, mainContext.getEvaluationContext());

            // 查找合适的审计策略
            AuditStrategy strategy = auditStrategies.stream()
                    .filter(s -> s.supports(dataAudit))
                    .findFirst()
                    .orElseGet(() -> new DefaultAuditStrategy());

            // 执行审计策略
            strategy.execute(context, result, dataAudit, oldEntity);

        } catch (Exception e) {
            log.error("处理审计失败: {}", dataAudit.description(), e);
        }
    }

    private AuditContext createAuditContext(DataAudit dataAudit, AuditContext mainContext, EvaluationContext evaluationContext) {
        AuditContext context = mainContext.copy();
        context.setOperationType(dataAudit.operationType());
        context.setTargetEntity(dataAudit.targetEntity().getSimpleName());
        context.setDescription(dataAudit.description());
        context.setBusinessKey(dataAudit.businessKey());
        context.setEvaluationContext(evaluationContext);
        context.setAuditLogService(auditLogService);
        context.setSpelParser(spelParser);

        // 设置targetId（对于非CREATE操作）
        if (ObjectUtil.equals(dataAudit.operationType(), OperationType.CREATE) &&
                !dataAudit.targetId().isEmpty()) {
            String targetId = spelParser.parseExpression(dataAudit.targetId()).getValue(evaluationContext, String.class);
            context.setTargetId(targetId);
        }

        return context;
    }

    private AuditContext prepareMainContext(ProceedingJoinPoint joinPoint, EvaluationContext evaluationContext) {
        AuditContext context = new AuditContext();
        context.setMethodName(joinPoint.getSignature().toShortString());
        context.setEvaluationContext(evaluationContext);
        context.setAuditLogService(auditLogService);
        context.setSpelParser(spelParser);

        // 设置操作人信息
        setOperatorInfo(context);

        // 设置客户端IP
        setClientIp(context);

        // 序列化方法参数
        try {
            String parameters = objectMapper.writeValueAsString(joinPoint.getArgs());
            context.setParameters(parameters);
        } catch (Exception e) {
            log.warn("序列化方法参数失败", e);
        }

        return context;
    }

    private Map<DataAudit, Object> preProcessOldEntities(DataAudit[] dataAudits, AuditContext mainContext, EvaluationContext evaluationContext) {
        Map<DataAudit, Object> oldEntities = new ConcurrentHashMap<>();

        for (DataAudit dataAudit : dataAudits) {
            if (dataAudit.operationType() == OperationType.UPDATE &&
                    dataAudit.enableOldEntityQuery()) {
                Object oldEntity = getOldEntityForUpdate(dataAudit, mainContext, evaluationContext);
                if (oldEntity != null) {
                    oldEntities.put(dataAudit, oldEntity);
                }
            }
        }
        return oldEntities;
    }

    private Object getOldEntityForUpdate(DataAudit dataAudit, AuditContext context, EvaluationContext evaluationContext) {
        // 从targetId表达式获取ID
        String targetId = spelParser.parseExpression(dataAudit.targetId()).getValue(evaluationContext, String.class);
        if (targetId == null || targetId.isEmpty()) {
            return null;
        }

        String cacheKey = dataAudit.targetEntity().getSimpleName() + ":" + targetId;
        return entityCache.computeIfAbsent(cacheKey, key -> {
            try {
                // 根据实体类型和ID查询旧数据
                return fetchEntityById(dataAudit.targetEntity(), targetId);
            } catch (Exception e) {
                log.warn("获取旧实体失败: {}", key, e);
                return null;
            }
        });
    }

    private Object fetchEntityById(Class<?> entityClass, String id) {
        // 这里需要根据实际业务实现
        log.debug("获取实体: {} ID: {}", entityClass.getSimpleName(), id);
        List<Object> mappers = MapperCacheManager.getMappersByEntity(entityClass);
        if(mappers.size() > 0) {
            if(mappers.get(0) instanceof BaseMapper) {
                BaseMapper mapper = (BaseMapper) mappers.get(0);
                return mapper.selectById(id);
            }
        }
        return null;
    }

    private EvaluationContext createEvaluationContext(ProceedingJoinPoint joinPoint) {
        return createEvaluationContext(joinPoint, null);
    }

    private EvaluationContext createEvaluationContext(ProceedingJoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();

        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(
                result, method, args, parameterNameDiscoverer);

        // 设置一些常用变量
        context.setVariable("result", result);
        context.setVariable("args", args);
        //设置业务中设置的变量
        Map<String, Object> vars = AuditContextHolder.getAll();
        if(MapUtil.isNotEmpty(vars)) {
            vars.forEach(context::setVariable);
        }
        return context;
    }

    private void setOperatorInfo(AuditContext context) {
        context.setOperatorId(operator.getOperatorId());
        context.setOperatorName(operator.getOperatorName());
        context.setTenantId(operator.getTenantId());
    }

    private void setClientIp(AuditContext context) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes)
                    RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                String ip = request.getHeader("X-Forwarded-For");
                if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("Proxy-Client-IP");
                }
                if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getHeader("WL-Proxy-Client-IP");
                }
                if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                    ip = request.getRemoteAddr();
                }
                context.setClientIp(ip);
            }
        } catch (Exception e) {
            log.warn("获取客户端IP失败", e);
        }
    }

}
